// Copyright 2017 Mobvoi Inc. All Rights Reserved.
// Author: congfu@mobvoi.com (Cong Fu)

#include "sdk/utils/os_support.h"

#ifdef MOBVOI_FIXED_POINT
#include "sdk/utils/fixed_utils.h"
#endif

#ifdef XTENSA_HIFI_MINI
#include "sdk/utils/hifi_mini_xtensa_utils.h"
#endif

#if defined(__QCC5121__) || defined(MOBVOI_QCC_ON_X86) || defined(ENABLE_FIXED_KWS)
int* fft_freq_data = NULL;
int* fft_tmpbuf_data = NULL;
int* fft_real = NULL;
int* fft_imag = NULL;
int* complexfft_br_buffer = NULL;

void init_fft_buffer(void) {
  if (fft_freq_data == NULL) {
    fft_freq_data = mobvoi_calloc(514 * sizeof(int));
  }
  if (fft_tmpbuf_data == NULL) {
    fft_tmpbuf_data = mobvoi_calloc(514 * sizeof(int));
  }
  if (fft_real == NULL) {
    fft_real = mobvoi_calloc(256 * sizeof(int));
  }
  if (fft_imag == NULL) {
    fft_imag = mobvoi_calloc(256 * sizeof(int));
  }
  if (complexfft_br_buffer == NULL) {
    complexfft_br_buffer = mobvoi_calloc(256 * sizeof(int));
  }
}

void free_fft_buffer(void) {
  if (fft_freq_data) {
    mobvoi_free(fft_freq_data);
    fft_freq_data = NULL;
  }

  if (fft_tmpbuf_data) {
    mobvoi_free(fft_tmpbuf_data);
    fft_tmpbuf_data = NULL;
  }

  if (fft_real) {
    mobvoi_free(fft_real);
    fft_real = NULL;
  }

  if (fft_imag) {
    mobvoi_free(fft_imag);
    fft_imag = NULL;
  }

  if (complexfft_br_buffer) {
    mobvoi_free(complexfft_br_buffer);
    complexfft_br_buffer = NULL;
  }
}
#endif

#if !defined(__QCC5121__) && !defined(__STM32__) && !defined(__CEVA__) && \
    !defined(__BES_CORTEX_M4__) && !defined(_MSC_VER) && !defined(__CEVAX__) && \
    !defined(__QUECTEL_ASR_KERNEL__) && !defined(__CEVABX__)
#include <pthread.h>

#if !defined(__ANDROID__)
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#endif

#endif

#if defined(XTENSA_HIFI4)
#include "sdk/utils/xtensa_utils.h"
#endif

#if defined(__ANDROID__)
#include <android/log.h>
#endif

// #define SHOW_ALLOC_INFO
#define LOG_MAX_LEN     512
#define LOG_TAG         "MOBVOI_DSP"
#if defined(__THREAD_X__)
static void* log_fp = NULL;
#endif

#if !defined(MOBVOI_DSP_CHIPSET) || defined(__THREAD_X__)
void mobvoi_log(char type, const char* file, int line, const char* fmt, ...) {
  va_list vl;

  char tmp[LOG_MAX_LEN] = {0};
  char s[LOG_MAX_LEN] = {0};
  int offset = 0;
#if !defined(__ANDROID__) && !defined(__THREAD_X__)
  s[offset++] = type;

  struct timeval tv;
  struct tm timeinfo;
  gettimeofday(&tv, NULL);
  localtime_r(&tv.tv_sec, &timeinfo);
  snprintf(s + offset, LOG_MAX_LEN - offset,
           "%02d%02d %02d:%02d:%02d.%06u",
           timeinfo.tm_mon + 1,
           timeinfo.tm_mday,
           timeinfo.tm_hour,
           timeinfo.tm_min,
           timeinfo.tm_sec,
           (unsigned) (tv.tv_usec / 100));
  offset += 20;

#ifdef MOBVOI_MULTI_THREAD
  snprintf(tmp, LOG_MAX_LEN, " %d(%lld) ", getpid(), (long long) pthread_self());
  snprintf(s + offset, LOG_MAX_LEN - offset, "%s", tmp);
  offset += strlen(tmp);
#endif
#endif

#ifdef LOG_FILE_INFO
#define FILE_PATH_LEN 32
  snprintf(tmp, LOG_MAX_LEN, "%s:%d]", file, line);
  int len_tmp = strlen(tmp);
  int offset_tmp = len_tmp > FILE_PATH_LEN ? (len_tmp - FILE_PATH_LEN) : 0;
  snprintf(s + offset, LOG_MAX_LEN - offset, "%s ", tmp + offset_tmp);
  offset += (len_tmp - offset_tmp + 1);
#endif

#if !defined(__ANDROID__) && !defined(__THREAD_X__)
  snprintf(s + offset, LOG_MAX_LEN - offset, "%s ", LOG_TAG);
  offset += strlen(LOG_TAG) + 1;
#endif

  va_start(vl, fmt);
  vsnprintf(s + offset, LOG_MAX_LEN - offset, fmt, vl);
  va_end(vl);

  int str_len = strlen(s);
  mobvoi_assert(str_len < sizeof(s) / sizeof(s[0]));

#if defined(__ANDROID__)
  int ltype = ANDROID_LOG_INFO;
  if (type == 'W') ltype = ANDROID_LOG_WARN;
  else if (type == 'E') ltype = ANDROID_LOG_ERROR;

#if defined(ENABLE_QUECTEL_PIPELINE)
  fprintf(stderr, "%s\n", s);
#else
  __android_log_print(ltype, LOG_TAG, "%s\n", s);
#endif

#elif defined(__THREAD_X__)
  if (log_fp == NULL) {
    log_fp = mobvoi_fopen("c:/mobvoi/log.txt", "wb");
  }

  if (log_fp) {
    mobvoi_fwrite(s, 1, str_len, log_fp);
    mobvoi_fwrite("\n", 1, 1, log_fp);
  }
#else
#if defined(__SHENLEI__) && defined(_MSC_VER)
  OutputDebugString(s);
#else
  fprintf(stderr, "%s\n", s);
#endif
#endif
}
#endif

#if defined(__STM32__)
char g_static_memory_basearr_[40960] = {0};
char *g_static_memory_base = g_static_memory_basearr_;
unsigned int g_static_memory_size = 40960;
#else
static char *g_static_memory_base[2] = {NULL, NULL};
static unsigned int g_static_memory_size[2] = {0, 0};
#endif

static int g_static_memory_allocated[2] = {0, 0};
static char *g_static_memory_free_ptr[2] = {NULL, NULL};
static unsigned int g_static_memory_align[2] = {4, 4};
static int g_dsp_memory_idx = -1;

void mobvoi_set_static_memory_base(int idx, void* base,
                                   unsigned int total,
                                   unsigned int align) {
  if (total == 0) return;

#ifdef XTENSA_HIFI_MINI
  // for optimization
  mobvoi_assert(align == 8);
#endif
  mobvoi_assert(idx < 2 && idx >=0);

  if (g_static_memory_base[idx] != NULL) {
    mobvoi_info("Memory base(%d) already set!", idx);
  }

  mobvoi_info("Set static memory(%d) %d", idx, total);

  g_static_memory_allocated[idx] = 0;

  g_static_memory_base[idx] = (char*) base;
  g_static_memory_free_ptr[idx] = (char*) base;
  g_static_memory_align[idx] = align;
  g_static_memory_size[idx] = total;
  memset(base, 0, total);
}

static char *g_shared_memory_base[2] = {NULL, NULL};
static unsigned int g_shared_memory_size[2] = {0, 0};
static unsigned int g_shared_memory_align[2] = {4, 4};
static unsigned int g_shared_memory_allocated[2] = {0, 0};
static unsigned int g_max_shared_memory_allocated[2] = {0, 0};
static char *g_shared_memory_free_ptr[2] = {NULL, NULL};
void mobvoi_set_shared_memory_base(int idx, void* base,
                                   unsigned int total,
                                   unsigned int align) {
  if (total == 0) return;

  mobvoi_assert(idx < 2 && idx >=0);

  mobvoi_info("Set shared memory(%d) %d", idx, total);
#ifdef XTENSA_HIFI_MINI
  // for optimization
  mobvoi_assert(align == 8);
#endif

  if (g_shared_memory_base[idx] != NULL) {
    mobvoi_info("Shared memory(%d) already set!", idx);
  }

  g_shared_memory_allocated[idx] = 0;

  g_shared_memory_base[idx] = (char*) base;
  g_shared_memory_free_ptr[idx] = NULL;
  g_shared_memory_align[idx] = align;
  g_shared_memory_size[idx] = total;
  memset(base, 0, total);
}

void mobvoi_begin_shared_memory(void) {
  if (g_dsp_memory_idx < 0) return;
  g_shared_memory_free_ptr[g_dsp_memory_idx] =
      g_shared_memory_base[g_dsp_memory_idx];
  g_shared_memory_allocated[g_dsp_memory_idx] = 0;
}

void mobvoi_end_shared_memory(void) {
  if (g_dsp_memory_idx < 0) return;
  g_shared_memory_free_ptr[g_dsp_memory_idx] = NULL;
}

void mobvoi_set_memory_index(int idx) {
  g_dsp_memory_idx = idx;
}

void mobvoi_reset_memory_base(int idx) {
  mobvoi_assert(idx < 2 && idx >=0);
  g_static_memory_base[idx] = NULL;
  g_static_memory_free_ptr[idx] = NULL;
  g_static_memory_size[idx] = 0;
  g_static_memory_align[idx] = 0;

  g_shared_memory_base[idx] = NULL;
  g_shared_memory_free_ptr[idx] = NULL;
  g_shared_memory_size[idx] = 0;
  g_shared_memory_align[idx] = 0;

#if defined(__THREAD_X__)
  if (log_fp) {
    mobvoi_fclose(log_fp);
    log_fp = NULL;
  }
#endif
}

static void *shared_malloc(int size) {
#ifdef MOBVOI_MULTI_THREAD
  static pthread_mutex_t mutex;
  pthread_mutex_lock(&mutex);
#endif

#ifdef SHOW_ALLOC_INFO
  mobvoi_info("Shared(%d) alloced %d, %d",
      g_dsp_memory_idx,
      g_shared_memory_allocated[g_dsp_memory_idx],
      size);
#endif

  mob_ptr align_mask = g_shared_memory_align[g_dsp_memory_idx] - 1;
  char* old_free_ptr = g_shared_memory_free_ptr[g_dsp_memory_idx];
  char *ptr = (char *) (((mob_ptr) old_free_ptr + align_mask) & ~align_mask);
  if (ptr - g_shared_memory_base[g_dsp_memory_idx] + size <=
      g_shared_memory_size[g_dsp_memory_idx]) {
    g_shared_memory_free_ptr[g_dsp_memory_idx] = ptr + size;
  } else {
    mobvoi_fatal("===============No free shared memory===============");
    return NULL;
  }

  g_shared_memory_allocated[g_dsp_memory_idx] += (size + ptr - old_free_ptr);

  if (g_shared_memory_allocated[g_dsp_memory_idx] >
      g_max_shared_memory_allocated[g_dsp_memory_idx]) {
    g_max_shared_memory_allocated[g_dsp_memory_idx] =
        g_shared_memory_allocated[g_dsp_memory_idx];
  }

#ifdef MOBVOI_MULTI_THREAD
  pthread_mutex_unlock(&mutex);
#endif

  return ptr;
}

static void *static_malloc(int size) {
#ifdef MOBVOI_MULTI_THREAD
  static pthread_mutex_t mutex;
  pthread_mutex_lock(&mutex);
#endif

#ifdef SHOW_ALLOC_INFO
  mobvoi_info("Static(%d) alloced %d, %d",
              g_dsp_memory_idx,
              g_static_memory_allocated[g_dsp_memory_idx],
              size);
#endif

  if (g_static_memory_free_ptr[g_dsp_memory_idx] == NULL) {
    g_static_memory_free_ptr[g_dsp_memory_idx] = g_static_memory_base[g_dsp_memory_idx];
  }

  mob_ptr align_mask = g_static_memory_align[g_dsp_memory_idx] - 1;
  char* old_free_ptr = g_static_memory_free_ptr[g_dsp_memory_idx];
  char *ptr = (char *)(((mob_ptr) old_free_ptr + align_mask) & ~align_mask);
  if (ptr - g_static_memory_base[g_dsp_memory_idx] + size <=
      g_static_memory_size[g_dsp_memory_idx]) {
    g_static_memory_free_ptr[g_dsp_memory_idx] = ptr + size;
    g_static_memory_allocated[g_dsp_memory_idx] += (size + ptr - old_free_ptr);
  } else {
    mobvoi_fatal("===============No free static memory===============");
#if defined(__STM32__)
    ////am_util_stdio_printf("memory is full ! \n\n");
    while (1);
#else
    return NULL;
#endif
  }

#ifdef MOBVOI_MULTI_THREAD
  pthread_mutex_unlock(&mutex);
#endif

  return ptr;
}

// Must initialize the memory to zero.
static void* mobvoi_calloc_(size_t size) {
#if defined(__QCC5121__)
#if defined(MOBVOI_DSP_DOWN) || defined(__QCC5141__)
  // mob_dsp_down/5141 do not use shared memory
  void* ptr = xzppmalloc(size, MOB_MEM_NONE);
#else
  void* ptr = xzppmalloc(size, MOB_MEM_SHARED);
#endif
  if (ptr == NULL) {
    ptr = xzppmalloc(size, MOB_MEM_NONE);
  }
  return ptr;
#elif defined(__THREAD_X__)
  void* ptr = NULL;
  InsUtil_Memory_Malloc(&ptr, size);
  memset(ptr, 0, size);
  return ptr;
#elif defined(__CEVA__)
  void* ptr = os_malloc(size);
  memset(ptr, 0, size);
  return ptr;
#elif defined(XTENSA_HIFI3)
  mobvoi_fatal("mobvoi_calloc NOT ready.");
  return NULL;
#elif defined(__CEVAX__)
  void* ptr = malloc(size);
  memset(ptr, 0, size);
  return ptr;
#else
  return calloc(size, 1);
#endif
}

static void* mobvoi_realloc_(void* ptr, size_t new_size) {
#if defined(__QCC5121__)
  mobvoi_fatal("mobvoi_realloc NOT ready.");
  return NULL;
#elif defined(__THREAD_X__)
  mobvoi_fatal("mobvoi_realloc NOT ready.");
  return NULL;
#elif defined(__CEVA__)
  mobvoi_fatal("mobvoi_realloc NOT ready.");
  return NULL;
#elif defined(XTENSA_HIFI3)
  mobvoi_fatal("mobvoi_realloc NOT ready.");
  return NULL;
#else
  return realloc(ptr, new_size);
#endif
}

static void mobvoi_free_(void* ptr) {
#ifdef __QCC5121__
  pfree(ptr);
#elif defined(__THREAD_X__)
  InsUtil_Memory_Free(ptr);
#elif defined(__CEVA__)
  os_mrelease(ptr);
#elif defined(XTENSA_HIFI3)
  mobvoi_fatal("mobvoi_free NOT ready.");
#else
  free(ptr);
#endif
}

void mobvoi_memory_summary(void) {
  mobvoi_assert(g_dsp_memory_idx == 0 || g_dsp_memory_idx == 1);
  mobvoi_info("Static %d memory used: %d Bytes",
              g_dsp_memory_idx,
              g_static_memory_allocated[g_dsp_memory_idx]);
  mobvoi_info("Shared %d memory used: %d Bytes",
              g_dsp_memory_idx,
              g_max_shared_memory_allocated[g_dsp_memory_idx]);
}

void *mobvoi_calloc(int size) {
  if (g_dsp_memory_idx >= 0 &&
      g_shared_memory_free_ptr[g_dsp_memory_idx] &&
      size > 24) {
    return shared_malloc(size);
  }

  if (g_dsp_memory_idx < 0 || g_static_memory_base[g_dsp_memory_idx] == NULL
#ifdef __QCC5121__
      || size >= 48
#endif
      ) {
#ifdef SHOW_ALLOC_INFO
    static int memory_alloc = 0;
    memory_alloc += size;
    mobvoi_info("Memory alloced %d, %d", memory_alloc, size);
#endif
    void* ptr = mobvoi_calloc_(size);
    mobvoi_assert(ptr != NULL);
    return ptr;
  }

  return static_malloc(size);
}

void* mobvoi_realloc(void* ptr, int size) {
  void* p = mobvoi_realloc_(ptr, size);
  mobvoi_assert(p != NULL);
  return p;
}

void mobvoi_free(void *ptr) {
  if (g_static_memory_base[0] != NULL &&
      (char*) ptr >= g_static_memory_base[0] &&
      (char*) ptr < g_static_memory_base[0] + g_static_memory_size[0]) {
    return;
  }
  if (g_static_memory_base[1] != NULL &&
      (char*) ptr >= g_static_memory_base[1] &&
      (char*) ptr < g_static_memory_base[1] + g_static_memory_size[1]) {
    return;
  }
  if (g_shared_memory_base[0] != NULL &&
      (char*) ptr >= g_shared_memory_base[0] &&
      (char*) ptr < g_shared_memory_base[0] + g_shared_memory_size[0]) {
    return;
  }
  if (g_shared_memory_base[1] != NULL &&
      (char*) ptr >= g_shared_memory_base[1] &&
      (char*) ptr < g_shared_memory_base[1] + g_shared_memory_size[1]) {
    return;
  }

  mobvoi_free_(ptr);
}

float normalize(int16 value) {
#ifdef MOBVOI_FIXED_POINT
  mobvoi_fatal("don't normalize for fixed point!");
  return 0;
#else
  return (float) value / kScalingFactor;
#endif
}

int16 denormalize(float value) {
#ifdef MOBVOI_FIXED_POINT
  mobvoi_fatal("don't denormalize for fixed point!");
  return 0;
#else
  float temp = value * kScalingFactor;
  if (temp > SHRT_MAX) temp = SHRT_MAX;
  else if (temp < SHRT_MIN) temp = SHRT_MIN;
  return (int16)temp;
#endif
}

int mobvoi_dsp_float_near_equal(mob_float A, mob_float B, mob_float tolerance) {
  if (A == B) return 1;
  mob_float error = A - B;
#ifdef MOBVOI_FIXED_POINT
  // Overflow can only happen if sign of a != sign of b, and then
  // it causes sign of diff != sign of a.
  if (((A ^ B) & 0x80000000UL) && ((A ^ error) & 0x80000000UL))
    return 0;
#endif
  error = error >= 0 ? error : -error;
  if (error <= tolerance) return 1;
  return 0;
}

int mobvoi_dsp_short_near_equal(int16 A, int16 B, int16 tolerance) {
  if (A == B) return 1;
  int error = (int) A - B;
  error = error >= 0 ? error : -error;
  if (error <= tolerance) return 1;
  return 0;
}

int mob_get_first_band(int start_freq, mob_float frame_len, int os_ratio) {
  int band = (int)((start_freq * frame_len * os_ratio) / 1000);
  return band == 0 ? 1 : band;
}

int mob_get_last_band(int stop_freq, mob_float frame_len, int os_ratio) {
  if (frame_len == 4) {
    if (stop_freq >= 7800 && stop_freq < 8000) {
      return 64;
    } else if (stop_freq == 24000) {
      // insta360
      return 191;
    } else {
      return (int)((stop_freq * frame_len * os_ratio) / 1000);
    }
  } else {
    return (int)((stop_freq * frame_len * os_ratio) / 1000 - 1);
  }
}

#ifdef MOBVOI_FIXED_POINT
// Q28
#define const_7div3 0x25555540
#define const_4div3 0x15555560
#define oneP5 0x17fffffe
#define zeroP5 0x07ffffff
// Q30
#define const_sqrt2 0x5a827980
#define const_1_Q30 0x40000000

// x ---- [0, 1.0f] --- Q31 in, Q28 out
int scl_sqrt(int x) {
#ifdef __CEVA__
  x = x << 1;
  int r = CEVA_TL3_sqrt_int32_asm(x, 0);  // Q32 in, Q16 out
  return r << (Q28 - Q16);
#else
  int x0, x1, x_ori;
  int i, e, sqrt_result;

  x_ori = x;

#ifdef XTENSA_HIFI_MINI
  e = 0;
  if (x < const_1_Q30) {
    ae_q56s t;
    t = AE_CVTQ48A32S(x);
    e = AE_NSAQ56S(t) - 8;
    x = x << e;
  }
#else
  e = 0;
  while (x < const_1_Q30) {  // x < 0.5
    Left_Shift(x, x, Q1);
    e = e + 1;
  }
#endif

#ifdef XTENSA_HIFI_MINI
  int64 acc;
  ae_q56s q0, q1, q2;
  acc = (int64)(const_4div3 >> (Q31 - Q16)) * (int64)x;
  q0 = AE_LQ56_I((const ae_q56s *)&acc, 0);
  q0 = AE_SATQ48S(q0);

  q1 = AE_CVTQ48A32S(const_7div3);
  q2 = AE_SUBQ56(q1, q0);
  q2 = AE_SATQ48S(q2);
  x0 = AE_TRUNCA32Q48(q2);
#else
  //x ---- [0.5, 1.0]
  Multiply_RightShift(const_4div3, x, x0, Q31); // Q28
  Sub_Op(const_7div3, x0, x0);                  // Q28
#endif
  for (i = 0; i < 3; i++) {
#ifdef XTENSA_HIFI_MINI
    acc = (int64)(zeroP5 >> (Q31 - Q16)) * (int64)x;
    q0 = AE_LQ56_I((const ae_q56s *)&acc, 0);
    q0 = AE_SATQ48S(q0);
    x1 = AE_TRUNCA32Q48(q0);

    acc = (int64)(x0 >> (Q31 - Q16)) * (int64)x1;
    q0 = AE_LQ56_I((const ae_q56s *)&acc, 0);
    q0 = AE_SLLASQ56S(q0, Q3);
    q0 = AE_SATQ48S(q0);
    x1 = AE_TRUNCA32Q48(q0);

    acc = (int64)(x0 >> (Q31 - Q16)) * (int64)x1;
    q0 = AE_LQ56_I((const ae_q56s *)&acc, 0);
    q0 = AE_SLLASQ56S(q0, Q3);
    q0 = AE_SATQ48S(q0);

    q1 = AE_CVTQ48A32S(oneP5);
    q2 = AE_SUBQ56(q1, q0);
    q2 = AE_SATQ48S(q2);
    x1 = AE_TRUNCA32Q48(q2);

    acc = (int64)(x0 >> (Q31 - Q16)) * (int64)x1;
    q0 = AE_LQ56_I((const ae_q56s *)&acc, 0);
    q0 = AE_SLLASQ56S(q0, Q3);
    q0 = AE_SATQ48S(q0);
    x0 = AE_TRUNCA32Q48(q0);
#else
    Multiply_RightShift(x, zeroP5, x1, Q31); // Q31 + Q28 - Q31 = Q28
    Multiply_RightShift(x1, x0, x1, Q31);    // Q28
    x1 = x1 << Q3;
    Multiply_RightShift(x1, x0, x1, Q31);    // Q28
    x1 = x1 << Q3;

    Sub_Op(oneP5, x1, x1);                   //Q28
    Multiply_RightShift(x1, x0, x1, Q31);    //Q28
    x0 = x1 << Q3;
#endif
  }
  // x1 ---- 1.0f / sqrt(x) ----> [1.0, 1.414]


  if (e != 0) {
    if (e & 1) {
#ifdef XTENSA_HIFI_MINI
      acc = (int64)(const_sqrt2 >> (Q31 - Q16)) * (int64)x0;
      q0 = AE_LQ56_I((const ae_q56s *)&acc, 0);
      q0 = AE_SLLASQ56S(q0, Q1);
      q0 = AE_SATQ48S(q0);
      x0 = AE_TRUNCA32Q48(q0);
#else
      Multiply_RightShift(x0, const_sqrt2, x0, Q31); // Q28 + Q30 - Q30 = Q28
      x0 = x0 << Q1;
#endif
      e = e - 1;
    }
    e = e >> 1;
  }

  // x * (1 / sqrt(x))
  // Q28 + Q31- Q31 = Q28
#ifdef XTENSA_HIFI_MINI
  acc = (int64)(x_ori >> ((Q31 - e) - Q16)) * (int64)x0;
  q0 = AE_LQ56_I((const ae_q56s *)&acc, 0);
  q0 = AE_SATQ48S(q0);
  sqrt_result = AE_TRUNCA32Q48(q0);
#else
  Multiply_RightShift(x0, x_ori, sqrt_result, (Q31 - e));
#endif
  return sqrt_result;  // Q28
#endif
}
#endif

int mob_get_band_ceil(mob_float freq, mob_float frame_len, mob_float os) {
#ifndef MOBVOI_FIXED_POINT
  return (int) ceilf(freq * frame_len * os / 1000.0f);
#else
  int x = freq * frame_len * os;
  int d = x / 1000;
  if ((d * 1000) < x) d++;
  return d;
#endif
}

int mob_get_band_floor(mob_float freq, mob_float frame_len, mob_float os) {
#ifndef MOBVOI_FIXED_POINT
  return (int) floorf(freq * frame_len * os / 1000.0f);
#else
  int x = freq * frame_len * os;
  return x / 1000;
#endif
}

#ifndef MOBVOI_FIXED_POINT
float mob_log10f(float x) {
#if defined (XTENSA_HIFI4)
  return scl_log10f(x);
#else
  return log10f(x);
#endif
}

float mob_logf(float x) {
#if defined (XTENSA_HIFI4)
  return scl_lognf(x);
#else
  return logf(x);
#endif
}

float mob_sinf(float x) {
#if defined (XTENSA_HIFI4)
  return scl_sinef(x);
#else
  return sinf(x);
#endif
}

float mob_cosf(float x) {
#if defined (XTENSA_HIFI4)
  return scl_cosinef(x);
#else
  return cosf(x);
#endif
}

float mob_powf(float x, float y) {
#if defined (XTENSA_HIFI4)
  float temp;
  temp = scl_lognf(x);
  temp = XT_MUL_S(temp, y);
  return scl_antilognf(temp);
#else
  return powf(x, y);
#endif
}
#endif

#if !defined(__QCC5121__) && !defined(__BES_CORTEX_M4__) && !defined(__CEVA__) && \
    !defined(__CEVAX__) && !defined(__CEVABX__)
void* mobvoi_fopen(const char* name, const char* mode) {
#ifdef __THREAD_X__
  return InsUtil_FS_Open(name, mode);
#else
  return fopen(name, mode);
#endif
}

int mobvoi_fclose(void* p) {
#ifdef __THREAD_X__
  return InsUtil_FS_Close(p);
#else
  return fclose(p);
#endif
}

uint64 mobvoi_fread(void* buff, uint64 size, uint64 count, void* p) {
#ifdef __THREAD_X__
  return InsUtil_FS_Read(buff, size, count, p);
#else
  return fread(buff, size, count, p);
#endif
}

uint64 mobvoi_fwrite(const void* buff, uint64 size, uint64 count, void* p) {
#ifdef __THREAD_X__
  return InsUtil_FS_Write(buff, size, count, p);
#else
  return fwrite(buff, size, count, p);
#endif
}
#endif

int mobvoi_get_device_id(unsigned char* device_id, int buff_len) {
#ifdef ENABLE_DAHUA_SPEAKER_PIPELINE
#ifdef __ANDROID__
  FILE* fp = fopen("/proc/cpuinfo", "rt");
#else
  FILE* fp = fopen("/tmp/cpuinfo", "rt");
#endif
  if (fp == NULL) return 0;

  int len = 0;
  char line[128] = {0};
  #define CPUINFO_SERIALNO_LEN 6
  while (fgets(line, sizeof(line), fp) != NULL) {
    if (memcmp(line, "Serial", CPUINFO_SERIALNO_LEN) != 0) continue;

    line[127] = 0;
    char* token = line + CPUINFO_SERIALNO_LEN;
    while ((*token > 0 && *token <= 32 ) || *token == ':') token++;

    if (*token != 0) {
      len = strlen(token);
      while(token[len - 1] == '\n' || token[len - 1] == '\r') len--;
      memset(device_id, 0, buff_len);
      strncpy(device_id, token, len);
      break;
    }
  }
  fclose(fp);
  return len;

// CHECK TRIAL_PERIOD condition in mobvoi_earbuds.c and bt addr in check_license !!!
#elif defined(ENABLE_EE172_PIPELINE) || defined(ENABLE_EE178_PIPELINE) || \
      defined(ENABLE_EE175_PIPELINE) || defined(ENABLE_OONTZ_PIPELINE) || \
      defined(ENABLE_NOTHING_PIPELINE) || defined(ENABLE_1000SHORES_PIPELINE)
#if defined(__BES_CORTEX_M4__)
  uint8_t* factory_section_get_bt_address();
  uint8_t *bt_addr = factory_section_get_bt_address();
  if (bt_addr == NULL) return 0;
#else
  // E44122 001234
  // uint8_t bt_addr[] = {0x34, 0x12, 0x00, 0x22, 0x41, 0xe4};
  // CCC012 345678
  uint8_t bt_addr[] = {0x78, 0x56, 0x34, 0x12, 0xC0, 0xCC};
#endif

  memcpy(device_id, bt_addr, 6);
  return 6;
#else
  return 0;
#endif
}

void smooth_3(mob_float* a, mob_float* b, int32 num) {
  mobvoi_assert(num >= 1);
  mobvoi_assert(a != b);
  b[0] = a[0];

#ifdef MOBVOI_FIXED_POINT
  if (num > 2) {
    int64 temp = 0;
    temp += a[0];
    temp += a[1];
    int last = 0;
    for (int i = 1; i < num - 1; i++) {
      temp -= last;
      temp += a[i + 1];
      last = a[i - 1];
      Div64_Op(temp, 3, b[i]);
    }
  }
#else
  for (int i = 1; i < num - 1; i++) {
    mob_float s = a[i - 1] + a[i] + a[i + 1];
    b[i] = s / 3;
  }
#endif

  b[num - 1] = a[num - 1];
}

void smooth_5(mob_float* a, mob_float* b, int32 num) {
  mobvoi_assert(num >= 3);
  mobvoi_assert(a != b);
  b[0] = a[0];

#ifdef MOBVOI_FIXED_POINT
  int64 temp = 0;
  temp += a[0];
  temp += a[1];
  temp += a[2];
  Div64_Op(temp, 3, b[1]);
  if (num > 4) {
    temp += a[3];
    int last = 0;
    for (int i = 2; i < num - 2; i++) {
      temp -= last;
      temp += a[i + 2];
      last = a[i - 2];
      Div64_Op(temp, 5, b[i]);
    }
  }

  temp = 0;
  temp += a[num - 3];
  temp += a[num - 2];
  temp += a[num - 1];
  Div64_Op(temp, 3, b[num - 2]);
#else
  b[1] = (a[0] + a[1] + a[2]) / 3;

  for (int i = 2; i < num - 2; i++) {
    mob_float s = a[i - 2] + a[i - 1] + a[i] + a[i + 1] + a[i + 2];
    b[i] = s / 5;
  }

  b[num - 2] = (a[num - 3] + a[num - 2] + a[num - 1]) / 3;
#endif

  b[num - 1] = a[num - 1];
}

// Same to matlab smooth.
void mobvoi_smooth(mob_float* a, mob_float* b, int32 num, int32 span) {
  mobvoi_assert(span <= num);
  mobvoi_assert(span >= 1);
  int32 half_span = (span - 1) / 2;
  for(int i = 0; i < num; ++i) {
    int cc = 1;
#ifdef MOBVOI_FIXED_POINT
#ifdef __CEVA__
    acc_t sum_h = 0, sum_l = a[i];
    for(int k = 1; k <= half_span && k <= i && k < (num - i); ++k) {
      int a1 = a[i - k];
      int a2 = a[i + k];
      add72(0, a1, sum_h, sum_l, sum_h, sum_l);
      add72(0, a2, sum_h, sum_l, sum_h, sum_l);
      cc += 2;
    }
    int div = CEVA_TL3_div32_uu(2147483648, cc);  // Q31
    acc_t r_lh, r_ll, r_hh, r_hl;
    int res;
    mpy72(sum_h, div, r_hh, r_hl);
    mpy72(sum_l, div, r_lh, r_ll);
    add72(r_hl, 0, r_lh, r_ll, r_lh, r_ll);
    shift72_ar(r_lh, r_ll, -Q31, res);
    b[i] = res;
#else
    int64 sum = a[i];
    for(int k = 1; k <= half_span && k <= i && k < (num - i); ++k) {
      sum += a[i-k];
      sum += a[i+k];
      cc += 2;
    }
    Div64_Op(sum, cc, b[i]);
#endif
#else
    float sum = a[i];
    for(int k = 1; k <= half_span; ++k) {
      if (i-k >= 0 && i+k < num) {
        sum += a[i-k];
        sum += a[i+k];
        cc += 2;
      }
    }
    b[i] = sum / cc;
#endif
  }
}

// kHanningN corresponds to hanning window which has 2*N+1 elements.
// The coefficients here are differences between consecutive elements.
// For example, kHanning5[] = {h(6)-h(5), h(5)-h(4), h(4)-h(3), h(3)-h(2), h(2)-h(1), h(1)}
mob_float kHanning2[] = {
    0.0833333f, 0.166667f, 0.0833333f
};
mob_float kHanning3[] = {
    0.0366117f, 0.0883883f, 0.0883883f, 0.0366117f
};
mob_float kHanning4[] = {
    0.0190983f, 0.05f, 0.0618034f, 0.05f, 0.0190983f
};
mob_float kHanning5[] = {
    0.0111645f, 0.0305021f, 0.0416667f, 0.0416667f, 0.0305021f, 0.0111645f
};
mob_float kHanning6[] = {
    0.00707365f, 0.0198199f, 0.0286406f, 0.0317887f, 0.0286406f, 0.0198199f,
    0.00707365f
};
mob_float kHanning7[] = {
    0.00475753f, 0.0135483f, 0.0202765f, 0.0239177f, 0.0239177f, 0.0202765f,
    0.0135483f, 0.00475753f
};
mob_float kHanning8[] = {
    0.00335041f, 0.00964712f, 0.0147802f, 0.0181307f, 0.0192942f, 0.0181307f,
    0.0147802f, 0.00964712f, 0.00335041f
};
mob_float kHanning9[] = {
    0.00244717f, 0.00710198f, 0.0110616f, 0.0139384f, 0.0154508f, 0.0154508f,
    0.0139384f, 0.0110616f, 0.00710198f, 0.00244717f
};
mob_float kHanning10[] = {
    0.00184123f, 0.00537452f, 0.0084724f, 0.0108839f, 0.0124136f, 0.0129377f,
    0.0124136f, 0.0108839f, 0.0084724f, 0.00537452f, 0.00184123f
};
mob_float kHanning11[] = {
    0.00141976f, 0.00416252f, 0.00662161f, 0.00862945f, 0.0100492f, 0.0107841f,
    0.0107841f, 0.0100492f, 0.00862945f, 0.00662161f, 0.00416252f, 0.00141976f
};
mob_float kHanning12[] = {
    0.00111762f, 0.00328792f, 0.00526713f, 0.00694023f, 0.00820999f, 0.00900262f,
    0.00927205f, 0.00900262f, 0.00820999f, 0.00694023f, 0.00526713f, 0.00328792f,
    0.00111762f
};
mob_float kHanning13[] = {
    0.000895432f, 0.00264139f, 0.00425491f, 0.00565506f, 0.00677165f, 0.00754867f,
    0.00794718f, 0.00794718f, 0.00754867f, 0.00677165f, 0.00565506f, 0.00425491f,
    0.00264139f, 0.000895432f
};
mob_float kHanning14[] = {
    0.000728413f, 0.0021534f, 0.00348428f, 0.00466288f, 0.00563769f, 0.0063661f,
    0.00681628f, 0.00696856f, 0.00681628f, 0.0063661f, 0.00563769f, 0.00466288f,
    0.00348428f, 0.0021534f, 0.000728413f
};
mob_float kHanning15[] = {
    0.00060046f, 0.0017783f, 0.00288781f, 0.00388634f, 0.00473552f, 0.00540271f,
    0.00586228f, 0.00609657f, 0.00609657f, 0.00586228f, 0.00540271f, 0.00473552f,
    0.00388634f, 0.00288781f, 0.0017783f, 0.00060046f
};

mob_float* kHanningDiffCoef[] = {
    kHanning2,
    kHanning3,
    kHanning4,
    kHanning5,
    kHanning6,
    kHanning7,
    kHanning8,
    kHanning9,
    kHanning10,
    kHanning11,
    kHanning12,
    kHanning13,
    kHanning14,
    kHanning15
};

void mobvoi_hann_smooth(mob_float* in, mob_float* out, int32 len, int32 max_hann_taps) {
  mobvoi_assert(max_hann_taps <= 15);
  mobvoi_assert(max_hann_taps >= 1);
  mobvoi_assert(len > 2 * max_hann_taps + 1);
  out[0] = in[0];
  out[1] = 0.25f * (in[0]+in[2]) + 0.5f * in[1];
  out[len-2] = 0.25f * (in[len-3]+in[len-1]) + 0.5f * in[len-2];
  out[len-1] = in[len-1];

  if(max_hann_taps == 1) {
    for (int i = 2; i < len-2; ++i) {
      out[i] = 0.25f * (in[i-1]+in[i+1]) + 0.5f * in[i];
    }
    return;
  }

  mob_float sum_buffer[16];
  for (int i = 2; i < max_hann_taps; ++i) {
    int current_tap = i;
    mob_float* coef = kHanningDiffCoef[current_tap-2];
    sum_buffer[0] = in[i];
    out[i] = coef[0] * sum_buffer[0];
    for (int k = 1; k <= current_tap; ++k) {
      sum_buffer[k] = sum_buffer[k-1] + in[i-k] + in[i+k];
      out[i] += coef[k] * sum_buffer[k];
    }
  }
  for (int i = max_hann_taps; i <= len-1-max_hann_taps; ++i) {
    int current_tap = max_hann_taps;
    mob_float* coef = kHanningDiffCoef[current_tap-2];
    sum_buffer[0] = in[i];
    out[i] = coef[0] * sum_buffer[0];
    for (int k = 1; k <= current_tap; ++k) {
      sum_buffer[k] = sum_buffer[k-1] + in[i-k] + in[i+k];
      out[i] += coef[k] * sum_buffer[k];
    }
  }
  for (int i = len-max_hann_taps; i < len-2; ++i) {
    int current_tap = len-1-i;
    mob_float* coef = kHanningDiffCoef[current_tap-2];
    sum_buffer[0] = in[i];
    out[i] = coef[0] * sum_buffer[0];
    for (int k = 1; k <= current_tap; ++k) {
      sum_buffer[k] = sum_buffer[k-1] + in[i-k] + in[i+k];
      out[i] += coef[k] * sum_buffer[k];
    }
  }
}

#if defined(__SHENLEI__) && defined(_MSC_VER)
#define LOG_MAX_LEN     512
#define LOG_TAG         "MOBVOI_DSP"
#include<windows.h>
void print_log(char type, const char* file, int line, const char* fmt, ...) {
    va_list vl;

    char tmp[LOG_MAX_LEN] = { 0 };
    char s[LOG_MAX_LEN] = { 0 };
    int offset = 0;

    s[offset++] = type;

    snprintf(s + offset, LOG_MAX_LEN - offset, "\n%s ", LOG_TAG);
    offset += strlen(LOG_TAG) + 1;

    va_start(vl, fmt);
    vsnprintf(s + offset, LOG_MAX_LEN - offset, fmt, vl);
    va_end(vl);

    int str_len = strlen(s);
    mobvoi_assert(str_len < sizeof(s) / sizeof(s[0]));
    OutputDebugStringA(s);
}
#endif
